package com.kye.express.business.scanner;

import android.Manifest;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.databinding.DataBindingUtil;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Pair;
import android.view.View;
import android.widget.Toast;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.jakewharton.rxbinding2.view.RxView;
import com.kye.base.DebounceObservableTransformer;
import com.kye.express.R;
import com.kye.express.business.base.BaseToolbarActivity;
import com.kye.express.business.order.scan_code_add_order.ScanCodeAddOrderActivity;
import com.kye.express.business.waybill.waybill_detail.WayBillDetailActivity;
import com.kye.express.databinding.ActivityWaybillScannerBinding;
import com.kye.express.utils.URLUtils;
import com.kye.express.view.KyeProgressDialog;
import com.kye.express.view.ToastManager;
import com.kye.model.SharedPreferencesManager;
import com.kye.model.bean.order.scan_code_order.ScanCodeOrderAddress;
import com.kye.model.bean.waybill_search.ResultWaybillQuery;
import com.kye.rxcodescanner.DecodeFormatManager;
import com.kye.rxcodescanner.RxCodeScanner;
import com.kye.rxcodescanner.RxCodeScannerFragment;
import com.tbruyelle.rxpermissions2.Permission;
import com.tbruyelle.rxpermissions2.RxPermissions;

import org.reactivestreams.Publisher;

import java.net.URL;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import io.reactivex.FlowableOnSubscribe;
import io.reactivex.Single;
import io.reactivex.SingleObserver;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.functions.Predicate;
import io.reactivex.processors.FlowableProcessor;
import io.reactivex.processors.PublishProcessor;
import io.reactivex.schedulers.Schedulers;

/**
 * 运单扫描
 */
public class WaybillScannerActivity extends BaseToolbarActivity implements WaybillScannerActivityContract.WaybillScannerView {
    private static final String KEY_SHOW_TIPS = "waybill_scanner_show_tips";
    private static final String KEY_TYPE = "KEY_TYPE";
    private static final int REQUEST_CODE_ALBUMS = 1;
    private Set<BarcodeFormat> mBarcodeFormatSet;

    private ActivityWaybillScannerBinding mBinding;

    private FlowableProcessor<Pair<String, String>> mScanResult = PublishProcessor.<Pair<String, String>>create().toSerialized();
    private Disposable mScanResultSubscription;


    private WaybillScannerActivityContract.WaybillScannerPresenter mPresenter;

    private Dialog mDialog;

    private RxPermissions rxPermissions;

    public static void startActivity(Context context, Type type) {
        context.startActivity(
                new Intent(context, WaybillScannerActivity.class)
                        .putExtra(KEY_TYPE, type)
        );
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_waybill_scanner);
        setUpToolbar((Toolbar) findViewById(R.id.toolbar));
        setTitle(getString(R.string.common_scanner));
        mPresenter = new WaybillScannerActivityContract.WaybillScannerPresenter();
        mPresenter.bind(this);
        rxPermissions = new RxPermissions(this);

        final Type type = (Type) getIntent().getSerializableExtra(KEY_TYPE);
        if (type == Type.WAYBILL) {
            mBarcodeFormatSet = DecodeFormatManager.CODE_128_FORMATS;
        } else {
            mBarcodeFormatSet = DecodeFormatManager.QR_CODE_FORMATS;
        }

        mBinding.setType(type);

        startScanner();
        showTipsView();
        RxView.clicks(mBinding.btnKnow)
                .compose(new DebounceObservableTransformer<>())
                .compose(this.bindToLifecycleDestroy())
                .subscribe(new Consumer<Object>() {
                    @Override
                    public void accept(Object o) throws Exception {
                        SharedPreferencesManager.commit(KEY_SHOW_TIPS, false);
                        showTipsView();
                    }
                });
    }

    private void showTipsView() {
        boolean showTips = SharedPreferencesManager.get(KEY_SHOW_TIPS, true);
        if (showTips) {
            mBinding.llTips.setVisibility(View.VISIBLE);
        } else {
            mBinding.llTips.setVisibility(View.GONE);
        }
    }

    private void startScanner() {
        final RxCodeScannerFragment rxCodeScannerFragment = RxCodeScanner.scanFromCamera(
                getSupportFragmentManager(),
                R.id.fragment,
                mBarcodeFormatSet
        );

        if (rxCodeScannerFragment != null) {
            rxCodeScannerFragment.setOnCodeScannerListener(new RxCodeScannerFragment.OnCodeScannerListener() {

                @Override
                public void onResultFlowableReady(Flowable<Pair<Result, ReaderException>> flowable) {
                    flowable.observeOn(Schedulers.computation())
                            .filter(new Predicate<Pair<Result, ReaderException>>() {
                                @Override
                                public boolean test(Pair<Result, ReaderException> resultReaderExceptionPair) throws Exception {
                                    return resultReaderExceptionPair.first != null &&
                                            !TextUtils.isEmpty(resultReaderExceptionPair.first.getText());
                                }
                            })
                            .map(new Function<Pair<Result, ReaderException>, String>() {
                                @Override
                                public String apply(Pair<Result, ReaderException> resultReaderExceptionPair) throws Exception {
                                    return resultReaderExceptionPair.first.getText();
                                }
                            })
                            .flatMap(new Function<String, Publisher<Pair<String, String>>>() {
                                @Override
                                public Publisher<Pair<String, String>> apply(String s) throws Exception {
                                    return Flowable.create(new FlowableOnSubscribe<Pair<String, String>>() {
                                        @Override
                                        public void subscribe(FlowableEmitter<Pair<String, String>> e) throws Exception {
                                            Map<String, List<String>> queryMap;

                                            try {
                                                queryMap = URLUtils.splitQuery(new URL(s));
                                            } catch (Throwable throwable) {
                                                queryMap = new LinkedHashMap<>();
                                                queryMap.put(null, Arrays.asList(s));
                                            }

                                            for (Map.Entry<String, List<String>> stringListEntry : queryMap.entrySet()) {
                                                String key = stringListEntry.getKey();
                                                List<String> value = stringListEntry.getValue();
                                                for (String s1 : value) {
                                                    e.onNext(Pair.create(key, s1));
                                                }
                                            }
                                        }
                                    }, BackpressureStrategy.DROP);
                                }
                            })
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe(mScanResult);

                    observeScanResult();
                }

                @Override
                public void onOpenTorch(boolean torch) {
                    if (torch) {
                        mBinding.ivLamp.setBackgroundResource(R.drawable.icon_scanner_bg_white);
                        mBinding.ivLamp.setImageResource(R.drawable.icon_scanner_lamp_open);
                        mBinding.tvLamp.setText(getString(R.string.waybill_scanner_close_lamp));
                    } else {
                        mBinding.ivLamp.setBackgroundResource(R.drawable.icon_scanner_bg_black);
                        mBinding.ivLamp.setImageResource(R.drawable.icon_scanner_lamp_close);
                        mBinding.tvLamp.setText(getString(R.string.waybill_scanner_open_lamp));
                    }
                }

                @Override
                public void needOpenTorch(boolean needOpenTorch) {
                    if (!needOpenTorch) {
                        rxCodeScannerFragment.setTorch(false);
                    }
                    mBinding.llLamp.setEnabled(needOpenTorch);
                }

                @Override
                public void onCameraOpened() {
                    Rect rect = mBinding.scanBoxView.getFramingRect();
                    rxCodeScannerFragment.setFocusLocation(rect.centerX(), rect.centerY());
                }
            });
            rxCodeScannerFragment.setContinuousFocus(true);

            RxView.clicks(mBinding.llLamp)
                    .compose(new DebounceObservableTransformer<>())
                    .compose(this.bindToLifecycleDestroy())
                    .subscribe(new Consumer<Object>() {
                        @Override
                        public void accept(Object o) throws Exception {
                            rxCodeScannerFragment.setTorch(!rxCodeScannerFragment.getTorch());
                        }
                    });

            RxView.clicks(mBinding.llAlbums)
                    .compose(new DebounceObservableTransformer<>())
                    .compose(this.bindToLifecycleDestroy())
                    .subscribe(new Consumer<Object>() {
                        @Override
                        public void accept(Object o) throws Exception {
                            selectFromAlbums();
                        }
                    });

            RxView.clicks(mBinding.llAlbums)
                    .compose(new DebounceObservableTransformer<>())
                    .compose(this.bindToLifecycleDestroy())
                    .compose(rxPermissions.ensureEach(Manifest.permission.WRITE_EXTERNAL_STORAGE))
                    .subscribe(new Consumer<Permission>() {
                        @Override
                        public void accept(Permission permission) throws Exception {
                            if (permission.granted) {
                                selectFromAlbums();
                            } else if (permission.shouldShowRequestPermissionRationale) {
                                //TODO
                                showToast("您已经拒绝了权限");
                            } else {
                                //TODO
                                showToast("您已经拒绝了权限");
                            }
                        }
                    });
        }
    }

    private void observeScanResult() {
        mScanResultSubscription = mScanResult.subscribe(new Consumer<Pair<String, String>>() {
            @Override
            public void accept(Pair<String, String> pair) throws Exception {
                if ("code".equals(pair.first) || null == pair.first) {
                    stopObserveScanResult();
                    if (null == pair.first) {
                        // 查询运单
                        mPresenter.searchWaybill(pair.second);
                    } else {
                        // 扫码下单
                        mPresenter.isCanAddOrder(pair.second);
                    }
                }
            }
        });
    }

    private void stopObserveScanResult() {
        if (mScanResultSubscription != null && !mScanResultSubscription.isDisposed()) {
            mScanResultSubscription.dispose();
            mScanResultSubscription = null;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopObserveScanResult();
        mPresenter.unbind();
    }

    @Override
    public void onLoading() {
        mDialog = KyeProgressDialog.show(this, mDialog);
    }

    @Override
    public void onLoaded() {
        KyeProgressDialog.dismiss(mDialog);
    }

    @Override
    public void onLoaded(ResultWaybillQuery datas, String searchString) {
        WayBillDetailActivity.startActivity(this, searchString);
    }

    @Override
    public void onCanScanCodeAddOrder(String customerCode, ScanCodeOrderAddress addressData) {
        ScanCodeAddOrderActivity.startActivity(this, customerCode, addressData);
        finish();
    }

    @Override
    public void onCanNotScanCodeAddOrder() {
        onError("你没有权限扫码下单");
    }

    @Override
    public void onError() {
        ToastManager.show(this, R.string.common_net_error);
        observeScanResult();
    }

    @Override
    public void onError(String msg) {
        ToastManager.show(this, msg, Toast.LENGTH_SHORT);
        observeScanResult();
    }

    private void showToast(String message) {
        ToastManager.show(this, message, Toast.LENGTH_SHORT);
    }

    private void selectFromAlbums() {
        Intent i = new Intent(
                Intent.ACTION_PICK,
                android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(i, REQUEST_CODE_ALBUMS);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (data != null) {
            switch (requestCode) {
                case REQUEST_CODE_ALBUMS:
                    Uri selectedImage = data.getData();
                    if (selectedImage != null) {
                        String[] filePathColumn = {MediaStore.Images.Media.DATA};
                        Cursor cursor = getContentResolver().query(selectedImage,
                                filePathColumn, null, null, null);
                        if (cursor != null) {
                            cursor.moveToFirst();
                            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
                            String picturePath = cursor.getString(columnIndex);
                            scannerByPath(picturePath);
                            cursor.close();
                        }
                    }
                    break;
            }
        }
    }

    private void scannerByPath(String imagePath) {
        if (!TextUtils.isEmpty(imagePath)) {
            Single<Result> result = RxCodeScanner.scanFormPath(imagePath, mBarcodeFormatSet);
            result
                    .compose(bindToLifecycleDestroy())
                    .subscribe(new SingleObserver<Result>() {
                        @Override
                        public void onSubscribe(Disposable d) {
                        }

                        @Override
                        public void onSuccess(Result result) {
                            if (result != null && !TextUtils.isEmpty(result.getText())) {
                                mPresenter.searchWaybill(result.getText());
                            } else {
                                showToast(getString(R.string.waybill_scanner_image_error));
                            }

                        }

                        @Override
                        public void onError(Throwable e) {
                            e.printStackTrace();
                            showToast(getString(R.string.waybill_scanner_image_error));
                        }
                    });

        } else {
            showToast(getString(R.string.waybill_scanner_image_select_faild));
        }
    }

    public enum Type {
        WAYBILL,
        SCAN_ADD_ORDER
    }
}
